package factories

import (
	"fmt"
	"gitee.com/captials-team/ubdframe/src/domain/dto"
	"gitee.com/captials-team/ubdframe/src/domain/models"
	"github.com/shopspring/decimal"
)

type MetricDataStater struct {
	dims []dto.TimeDim
}

func NewMetricDataStater(dims ...dto.TimeDim) *MetricDataStater {
	return &MetricDataStater{
		dims: dims,
	}
}

// MergeCalc 合并统计计算
func (st *MetricDataStater) MergeCalc(list []*models.MetricData) []*models.MetricStatData {
	return st.MergeCalcDims(list, st.dims)
}

// MergeCalcDims 合并统计计算（指定时间dim）
func (st *MetricDataStater) MergeCalcDims(list []*models.MetricData, dims []dto.TimeDim) []*models.MetricStatData {
	if len(dims) == 0 {
		return []*models.MetricStatData{}
	}

	var data []*models.MetricStatData
	for _, dim := range dims {
		tmp := st.MergeCalcOneDim(list, dim)
		data = append(data, tmp...)
	}

	return data
}

// MergeCalcOneDim 合并统计计算（指定时间dim）
func (st *MetricDataStater) MergeCalcOneDim(list []*models.MetricData, dim dto.TimeDim) []*models.MetricStatData {
	//当前统计颗粒度下，不同对齐时间的统计目标
	statMap := map[string]*statTarget{}
	for _, item := range list {
		alignTime := dim.TimeFormat(item.Timestamp) //对应颗粒度的对齐时间戳
		key := fmt.Sprintf("%d_%d", item.MetricId, alignTime)

		tmp, exist := statMap[key]
		if !exist {
			statMap[key] = &statTarget{
				MetricId:       item.MetricId,
				StatType:       dim.String(),
				AlignTimestamp: alignTime,
			}
			tmp = statMap[key]
		}
		tmp.Items = append(tmp.Items, item)
	}

	//计算
	for k, v := range statMap {
		n := st.summaryCalc(*v)
		statMap[k] = &n
	}
	//生成
	var data []*models.MetricStatData
	for _, v := range statMap {
		data = append(data, &models.MetricStatData{
			StatType:   v.StatType,
			MetricId:   v.MetricId,
			ValueLast:  v.Summary.LastValue,
			ValueMin:   v.Summary.MinValue.String(),
			ValueMax:   v.Summary.MaxValue.String(),
			ValueSum:   v.Summary.SumValue.String(),
			ValueCount: v.Summary.CountValue.String(),
			ValueAvg:   v.Summary.AvgValue.String(),
			Timestamp:  v.AlignTimestamp,
			UpdatedAt:  v.Summary.Timestamp,
		})
	}

	return data
}

// summaryCalc 汇总计算
func (st *MetricDataStater) summaryCalc(target statTarget) statTarget {
	var ret statTarget
	if len(target.Items) == 0 {
		return ret
	}
	ret = target
	ret.Summary.MaxValue = decimal.NewFromFloat(0)
	ret.Summary.MinValue = decimal.NewFromFloat(0)
	ret.Summary.SumValue = decimal.NewFromFloat(0)
	ret.Summary.CountValue = decimal.NewFromFloat(0)

	for i, v := range target.Items {
		//判断是否是数字，非数字不处理
		value, err := decimal.NewFromString(v.Value)
		//非数字就只处理count，值当做0进行处理
		if err != nil {
			value = decimal.NewFromFloat(0)
		}

		//初始化第一个点
		if i == 0 {
			ret.Summary.LastValue = v.Value
			ret.Summary.Timestamp = v.Timestamp
			ret.Summary.MaxValue = value
			ret.Summary.MinValue = value
			ret.Summary.SumValue = value
			ret.Summary.CountValue = ret.Summary.CountValue.Add(decimal.NewFromInt(1))
			continue
		}

		//最后一条数据
		if v.Timestamp > ret.Summary.Timestamp {
			ret.Summary.LastValue = v.Value
			ret.Summary.Timestamp = v.Timestamp
		}

		ret.Summary.CountValue = ret.Summary.CountValue.Add(decimal.NewFromInt(1))
		ret.Summary.SumValue = ret.Summary.SumValue.Add(value)
		if value.GreaterThan(ret.Summary.MaxValue) {
			ret.Summary.MaxValue = value
		}
		if value.LessThan(ret.Summary.MinValue) {
			ret.Summary.MinValue = value
		}

		ret.Summary.AvgValue = ret.Summary.SumValue.Div(ret.Summary.CountValue)
	}

	return ret
}

type statTarget struct {
	MetricId       int64
	StatType       string
	AlignTimestamp int64
	Summary        statSummary          //汇聚结果
	Items          []*models.MetricData //待汇聚计算的items
}

// 统计结果
type statSummary struct {
	LastValue  string
	MaxValue   decimal.Decimal
	MinValue   decimal.Decimal
	SumValue   decimal.Decimal
	CountValue decimal.Decimal
	AvgValue   decimal.Decimal
	Timestamp  int64
}
